#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include "errcode.h"
#include <execinfo.h>

extern int done;
extern int zucOperatorError(int id, const char* fmt, ...);
extern int zucOperatorText(int id, const char* fmt, ...);

void backtrace(int signo)
{
    char pid_buf[30];
    char name_buf[512];
    char filename[512];

    // signal(signo, SIG_IGN);  // prevent multiple invocations on same signal
    sprintf(pid_buf, "%d", getpid());
    sprintf(filename, "/tmp/backtrace.%s", pid_buf);

    name_buf[readlink("/proc/self/exe", name_buf, 511)] = 0;
    int child_pid = fork();
    if (!child_pid)
    {
        fclose(stderr);
        stderr = freopen(filename, "a", stderr);
        dup2(2, 1);  // child: redirect output to stderr
        fprintf(stdout, "stack trace for %s pid=%s signal=%d\n", name_buf, pid_buf, signo);
        execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
        zucOperatorError(MISCELLANEOUS_ERROR, "backtrace for %s (pid %s signal %d): gdb failed to start", name_buf, pid_buf, signo);
        abort(); /* If gdb failed to start */
    }
    else
    {
        int status;
        waitpid(child_pid, &status, 0);
        if (signo == SIGUSR1)
        {  // continue running after backtrace
            signal(SIGUSR1, backtrace);
            zucOperatorText(0, "backtrace for %s stored in %s, continuing", name_buf, filename);
            fprintf(stderr, "%s: backtrace stored in %s, continuing\n", name_buf, filename);
        }
        else
        {
            // this takes zucmodule.cc:ZUC_COMMAND_TIMEOUT seconds to display:
            if (status == 0)  // backtrace succeeded
                zucOperatorError(MISCELLANEOUS_ERROR, "%s (pid %d) died on signal %d, backtrace stored in %s", name_buf, getpid(), signo, filename);
            fprintf(stderr, "%s exiting, traceback %s\n", name_buf, filename);
            done = 1;  // signal task to exit main loop
        }
    }
}

void backtraces_new(int sig)
{
    signal(sig, SIG_IGN);  // prevent multiple invocations on same signal

    void* array[40] = {0};
    size_t size = backtrace(array, 40);

    char pid_buf[30];
    char name_buf[512];
    char filename[512];

    backtrace(sig);

    sprintf(pid_buf, "%d", getpid());
    sprintf(filename, "/tmp/backtrace_new.%s", pid_buf);

    name_buf[readlink("/proc/self/exe", name_buf, 511)] = 0;
    int child_pid = fork();
    if (!child_pid)
    {
        fclose(stderr);
        stderr = freopen(filename, "a", stderr);
        dup2(2, 1);  // child: redirect output to stderr
        fprintf(stdout, "stack trace for %s pid=%s signal=%d\n", name_buf, pid_buf, sig);
        backtrace_symbols_fd(array, size, STDOUT_FILENO);
    }
    else
    {
        int status;
        waitpid(child_pid, &status, 0);
        if (sig == SIGUSR1)
        {  // continue running after backtrace
            signal(SIGUSR1, backtrace);
            fprintf(stderr, "%s: backtrace stored in %s, continuing\n", name_buf, filename);
        }
        else
        {
            // this takes zucmodule.cc:ZUC_COMMAND_TIMEOUT seconds to display:
            if (status == 0)  // backtrace succeeded
                fprintf(stderr, "%s exiting, traceback %s\n", name_buf, filename);
            done = 1;  // signal task to exit main loop
        }
    }

    exit(-1);
}
